﻿using System.IO;
using System;
using Microsoft.Win32.SafeHandles;

namespace LFSRecord2.Misc
{
    /// <summary>
    /// Extension of the FileStream class with endian aware binary read methods.
    /// </summary>
    public class EndianFileStream : FileStream
    {
        /// <summary>
        /// Indicates whether the stream uses big or little endian
        /// </summary>
        public bool IsLittleEndian { get; private set; }
        byte[] buf = new byte[8];

        /// <summary>
        /// Initialises a new instance of the EndianFileStream class with the specified path, creation mode and endian type.
        /// </summary>
        /// <param name="path">The absolute path to the file</param>
        /// <param name="mode">FileMode indicator</param>
        /// <param name="endian">Endian indicator</param>
        public EndianFileStream(string path, FileMode mode, EndianTypes endian)
            : base(path, mode)
        {
            IsLittleEndian = endian == EndianTypes.Little;
        }

        /// <summary>
        /// Initialises a new instance of the EndianFileStream class with the specified stream handle, access mode and endian type.
        /// </summary>
        /// <param name="path">Handle to an already opened file</param>
        /// <param name="mode">FileAccess indicator</param>
        /// <param name="endian">Endian indicator</param>
        public EndianFileStream(SafeFileHandle handle, FileAccess access, EndianTypes endian)
            : base(handle, access)
        {
            IsLittleEndian = endian == EndianTypes.Little;
        }

        public int ReadInt16()
        {
            setBuf(2);
            return BitConverter.ToInt16(buf, 0);
        }

        public int ReadInt32()
        {
            setBuf(4);
            return BitConverter.ToInt32(buf, 0);
        }

        public int ReadInt64()
        {
            setBuf(4);
            return BitConverter.ToInt32(buf, 0);
        }

        public ushort ReadUInt16()
        {
            setBuf(2);
            return BitConverter.ToUInt16(buf, 0);
        }

        public uint ReadUInt32()
        {
            setBuf(4);
            return BitConverter.ToUInt32(buf, 0);
        }

        public uint ReadUInt64()
        {
            setBuf(8);
            return BitConverter.ToUInt32(buf, 0);
        }

        public char[] ReadChars(int count)
        {
            char[] cBuf = new char[count];
            for (int a = 0; a < count; a++)
            {
                cBuf[a] = (char)ReadByte();
            }
            return cBuf;
        }

        public void ReadBytes(byte[] array, int offset, int count)
        {
            count += offset;
            if (IsLittleEndian)
            {
                count--;
                for (int a = count; a >= offset; a--)
                {
                    array[a] = (byte)ReadByte();
                } 
            }
            else
            {
                for (int a = offset; a < count; a++)
                {
                    array[a] = (byte)ReadByte();
                }
            }
        }

        void setBuf(int size)
        {
            if (IsLittleEndian)
            {
                size--;
                for (int a = size; a >= 0; a--)
                {
                    buf[a] = (byte)ReadByte();
                }
            }
            else
            {
                for (int a = 0; a < size; a++)
                {
                    buf[a] = (byte)ReadByte();
                }
            }
        }
    }
}
